package top.easyblog.core.util;


import org.apache.commons.lang3.StringUtils;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;

/**
 * @author: frank.huang
 * @date: 2023-12-21 22:52
 */
public class SpELUtils {
    private static final String EMPTY_STRING = StringUtils.EMPTY;

    /**
     * 支持 #p0 参数索引的表达式解析
     *
     * @param rootObject 根对象，method 所在的对象
     * @param spel       表达式
     * @param method     目标方法
     * @param args       方法入参
     * @return 解析后的字符串
     */
    public static String parse(Object rootObject, String spel, Method method, Object[] args) {
        if (StringUtils.isBlank(spel)) {
            return EMPTY_STRING;
        }

        // 获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);

        if (Objects.isNull(parameterNames) || parameterNames.length == 0) {
            return spel;
        }

        // 使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();

        // SPEL上下文
        StandardEvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, args, parameterNameDiscoverer);

        // 把方法参数放入SPEL上下文中
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }

        Object value = parser.parseExpression(spel).getValue(context, Object.class);
        return convertResultToString(value);
    }

    @SuppressWarnings("unchecked")
    private static String convertResultToString(Object value) {
        String result = EMPTY_STRING;

        if (value instanceof List<?>) {
            List<Object> values = (List<Object>) value;
            StringJoiner joiner = new StringJoiner(",");
            values.stream().filter(Objects::nonNull).forEach(item -> joiner.add(String.valueOf(item)));
            result = joiner.toString();
        } else {
            result = String.valueOf(value);
        }

        return result;
    }
}

